﻿namespace JoinBox.MathEx.TSP;


using System;
using System.Collections.Generic;
using System.Linq;


/// <summary>
/// 旅行商队伍
/// </summary>
public class Colony<TGeneticInfo> // : SelectModeMethod<Tour>
     where TGeneticInfo : GeneticInfoBasal, new()
{
    #region 成员
    /// <summary>
    /// 旅行商集合
    /// </summary>
    readonly List<Tour> _Tours;
    readonly TGeneticInfo _Info;

    /// <summary>
    /// 最大概率
    /// </summary>
    public double FitMax { get; private set; }
    #endregion

    #region 构造
    /// <summary>
    /// 旅行商队伍
    /// </summary>
    /// <param name="tours">旅行商集合</param>
    public Colony(List<Tour> tours, TGeneticInfo info)
    {
        _Info  = info;
        _Tours = tours;
        FitMax = GetFitMax();
    }
    #endregion

    #region 方法
    /// <summary>
    /// 最大的适应度
    /// </summary>
    /// <returns></returns>
    private double GetFitMax()
    {
        return _Tours.Max(t => t.Fitness);
    }

    /// <summary>
    /// 挑选精英
    /// </summary>
    /// <param name="n"></param>
    /// <returns></returns>
    public Colony<TGeneticInfo> Elite(int n)
    {
        List<Tour> best = new();                              // 最好的
        var tmp = new Colony<TGeneticInfo>(_Tours, _Info);    // 临时的旅行商群

        for (int i = 0; i < n; ++i)
        {
            var tour = tmp.FindBest();
            if (tour is not null)
                best.Add(tour);
            tmp = new Colony<TGeneticInfo>(tmp._Tours.Except(best).ToList(), _Info);
        }
        return new Colony<TGeneticInfo>(best, _Info);
    }

    /// <summary>
    /// 找到最好的
    /// </summary>
    /// <returns></returns>
    public Tour? FindBest()
    {
        Tour? tour = null;
        foreach (var item in _Tours)
            if (item.Fitness == FitMax)
            {
                tour = item;
                break;
            }
        return tour;
    }

    /// <summary>
    /// 进化
    /// </summary>
    /// <param name="elitism">精英数量(染色体数量)</param>
    /// <returns></returns>
    public Colony<TGeneticInfo> Evolve(int elitism)
    {
        int count = _Tours.Count - elitism;
        if (count < 1)
            throw new ArgumentException("精英数量不能大于旅行商数量");

        var best = Elite(elitism);
        var np = GenNewColony(count);// Env.ColonySize用Tours.Count代替
        var tours = best._Tours.Concat(np._Tours).ToList();
        return new Colony<TGeneticInfo>(tours, _Info);
    }

    public Tour SelectSortBubble()
    {
        return SelectTournament();//////////
    }

    public Tour SelectRoulette()
    {
        return SelectTournament();//////////
    }

    public Tour SelectTournament()
    {
        while (true)
        {
            int i = GeneticInfoBasal.Random.Next(0, _Tours.Count);// Env.ColonySize用Tours.Count代替

            if (GeneticInfoBasal.Random.NextDouble() < _Tours[i].Fitness / FitMax)
                return new Tour(_Tours[i].Citys);
        }
    }

    /// <summary>
    /// 更新旅行商队伍
    /// </summary>
    /// <param name="n"></param>
    /// <returns></returns>
    public Colony<TGeneticInfo> GenNewColony(int n)
    {
        List<Tour> tours = new();
        for (int i = 0; i < n; ++i)
        {
            // 选择两个优秀的旅行商交配生孩子
            Tour? son = null;
            switch (_Info.SelectMode)
            {
                case SelectMode.SortBubble:
                    son = SelectSortBubble().Crossover(SelectSortBubble());
                    break;
                case SelectMode.Tournament:
                    son = SelectTournament().Crossover(SelectTournament());
                    break;
                case SelectMode.Roulette:
                    son = SelectRoulette().Crossover(SelectRoulette());
                    break;
            }
            // 孩子产生变异,
            // 城市就是遗传信息染色体Bits,变异就是交换了城市链的顺序
            if (son is not null)
                tours.Add(son.Mutate(_Info.MutateProbability));
        }
        return new Colony<TGeneticInfo>(tours, _Info);
    }
    #endregion
}